# Copyright (C) 2023 Andrew Lutsenko <anlutsenko@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, you may find one here:
# https://www.gnu.org/licenses/gpl-3.0.html
# or you may search the http://www.gnu.org website for the version 3 license,
# or you may write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA


from app import strings
from app.handlers import CodeIssueHandler
from unittest.mock import patch

from .handler_test_base import HandlerTestBase


@patch("app.handlers.code_issue.KICAD_VERSION", new="6.0.10")
class TestCodeIssueHandler(HandlerTestBase):
    @patch("app.handlers.code_issue.get_version")
    def testLabelVersion_noVersion(self, get_version):
        get_version.return_value = ""
        handler = CodeIssueHandler(self.gitlab)
        assert not handler.labelVersion(self.issue)
        assert not self.issue.labels

    @patch("app.handlers.code_issue.get_version")
    def testLabelVersion_version699(self, get_version):
        get_version.return_value = "6.99"
        handler = CodeIssueHandler(self.gitlab)
        assert handler.labelVersion(self.issue)
        assert "7.0" in self.issue.labels

    @patch("app.handlers.code_issue.get_version")
    def testLabelVersion_version60(self, get_version):
        get_version.return_value = "6.0.10"
        handler = CodeIssueHandler(self.gitlab)
        assert handler.labelVersion(self.issue)
        assert "6.0" in self.issue.labels

    @patch("app.handlers.code_issue.get_version")
    @patch("app.handlers.code_issue.has_build_info")
    @patch("app.handlers.code_issue.fix_build_info")
    def testHandle_openIssue(self, fix_build_info, has_build_info, get_version):
        fix_build_info.return_value = "fixed info"
        has_build_info.return_value = True
        get_version.return_value = "6.0.10"
        payload = {
            "project": {
                "id": 123,
            },
            "object_attributes": {
                "iid": 12345,
                "action": "open",
                "assignee_id": None,
            },
        }

        handler = CodeIssueHandler(self.gitlab)
        assert not handler.handle("Issue Hook", payload)

        assert self.issue.description == "fixed info"
        assert "6.0" in self.issue.labels
        assert "status::new" in self.issue.labels
        assert "priority::undecided" in self.issue.labels
        assert not hasattr(self.issue, "state_event")
        self.issue.save.assert_called_once_with()

    @patch("app.handlers.code_issue.get_version")
    @patch("app.handlers.code_issue.has_build_info")
    @patch("app.handlers.code_issue.fix_build_info")
    def testHandle_openIssue_noVersion(self, fix_build_info, has_build_info, get_version):
        has_build_info.return_value = False
        payload = {
            "project": {
                "id": 123,
            },
            "object_attributes": {
                "iid": 12345,
                "action": "open",
                "assignee_id": None,
            },
        }

        handler = CodeIssueHandler(self.gitlab)
        assert not handler.handle("Issue Hook", payload)

        assert "status::incomplete-report" in self.issue.labels
        assert "priority::undecided" in self.issue.labels
        assert self.issue.state_event == "close"
        self.issue.save.assert_called_once_with()
        self.issue.notes.create.assert_called_once_with(
            {'body': strings.ISSUE_MISSING_VERSION_INFO})

    @patch("app.handlers.code_issue.get_version")
    @patch("app.handlers.code_issue.has_build_info")
    @patch("app.handlers.code_issue.fix_build_info")
    def testHandle_openIssue_malformedVersion(self, fix_build_info, has_build_info, get_version):
        has_build_info.return_value = True
        get_version.return_value = ""
        payload = {
            "project": {
                "id": 123,
            },
            "object_attributes": {
                "iid": 12345,
                "action": "open",
                "assignee_id": None,
            },
        }

        handler = CodeIssueHandler(self.gitlab)
        assert not handler.handle("Issue Hook", payload)

        assert "status::need-info" in self.issue.labels
        assert "priority::undecided" in self.issue.labels
        assert not hasattr(self.issue, "state_event")
        self.issue.save.assert_called_once_with()
        self.issue.notes.create.assert_called_once_with(
            {'body': strings.ISSUE_MALFORMED_VERSION_INFO})

    @patch("app.handlers.code_issue.get_version")
    @patch("app.handlers.code_issue.has_build_info")
    @patch("app.handlers.code_issue.fix_build_info")
    def testHandle_openIssue_oldVersion(self, fix_build_info, has_build_info, get_version):
        has_build_info.return_value = True
        get_version.return_value = "6.0.3"
        payload = {
            "project": {
                "id": 123,
            },
            "object_attributes": {
                "iid": 12345,
                "action": "open",
                "assignee_id": None,
            },
        }

        handler = CodeIssueHandler(self.gitlab)
        assert not handler.handle("Issue Hook", payload)

        assert "status::outdated" in self.issue.labels
        assert "priority::undecided" in self.issue.labels
        assert self.issue.state_event == "close"
        self.issue.save.assert_called_once_with()
        self.issue.notes.create.assert_called_once_with(
            {'body': strings.ISSUE_NEWER_KICAD_AVAILABLE})

    @patch("app.handlers.code_issue.get_version")
    @patch("app.handlers.code_issue.has_build_info")
    @patch("app.handlers.code_issue.fix_build_info")
    def testHandle_openIssue_correctVersion(self, fix_build_info, has_build_info, get_version):
        has_build_info.return_value = True
        get_version.return_value = "6.0.10"
        payload = {
            "project": {
                "id": 123,
            },
            "object_attributes": {
                "iid": 12345,
                "action": "open",
                "assignee_id": None,
            },
        }

        handler = CodeIssueHandler(self.gitlab)
        assert not handler.handle("Issue Hook", payload)

        assert "6.0" in self.issue.labels
        assert "priority::undecided" in self.issue.labels
        assert not hasattr(self.issue, "state_event")
        self.issue.save.assert_called_once_with()
        self.issue.notes.create.assert_not_called()

    @patch("app.handlers.code_issue.get_version")
    @patch("app.handlers.code_issue.has_build_info")
    @patch("app.handlers.code_issue.fix_build_info")
    def testHandle_openIssue_buildError(self, fix_build_info, has_build_info, get_version):
        self.issue.description = "Some build-error occured"
        fix_build_info.return_value = ""
        has_build_info.return_value = False
        payload = {
            "project": {
                "id": 123,
            },
            "object_attributes": {
                "iid": 12345,
                "action": "open",
                "assignee_id": None,
            },
        }

        handler = CodeIssueHandler(self.gitlab)
        assert not handler.handle("Issue Hook", payload)

        assert "build-error" in self.issue.labels
        assert "priority::undecided" in self.issue.labels
        assert not hasattr(self.issue, "state_event")
        self.issue.save.assert_called_once_with()

    @patch("app.handlers.code_issue.get_version")
    @patch("app.handlers.code_issue.has_build_info")
    @patch("app.handlers.code_issue.fix_build_info")
    def testHandle_openIssue_sentry(self, fix_build_info, has_build_info, get_version):
        self.issue.description = "Sentry Issue: blah"
        fix_build_info.return_value = ""
        has_build_info.return_value = False
        payload = {
            "project": {
                "id": 123,
            },
            "object_attributes": {
                "iid": 12345,
                "action": "open",
                "assignee_id": None,
            },
        }

        handler = CodeIssueHandler(self.gitlab)
        assert not handler.handle("Issue Hook", payload)

        assert "status::new" in self.issue.labels
        assert "priority::undecided" in self.issue.labels
        assert not hasattr(self.issue, "state_event")
        self.issue.save.assert_called_once_with()

    @patch("app.handlers.code_issue.get_version")
    @patch("app.handlers.code_issue.has_build_info")
    @patch("app.handlers.code_issue.fix_build_info")
    def testHandle_openIssue_assigned(self, fix_build_info, has_build_info, get_version):
        fix_build_info.return_value = ""
        has_build_info.return_value = False
        payload = {
            "project": {
                "id": 123,
            },
            "object_attributes": {
                "iid": 12345,
                "action": "open",
                "assignee_id": 777,
            },
        }

        handler = CodeIssueHandler(self.gitlab)
        assert not handler.handle("Issue Hook", payload)

        assert "status::new" in self.issue.labels
        assert "priority::undecided" in self.issue.labels
        assert not hasattr(self.issue, "state_event")
        self.issue.save.assert_called_once_with()

    @patch("app.handlers.code_issue.get_version")
    @patch("app.handlers.code_issue.has_build_info")
    @patch("app.handlers.code_issue.fix_build_info")
    def testHandle_reopenIssue_correctVersion(self, fix_build_info, has_build_info, get_version):
        self.issue.labels = ["status::outdated"]
        fix_build_info.return_value = ""
        has_build_info.return_value = True
        get_version.return_value = "6.0.10"
        payload = {
            "project": {
                "id": 123,
            },
            "object_attributes": {
                "iid": 12345,
                "action": "reopen",
                "assignee_id": None,
            },
        }

        handler = CodeIssueHandler(self.gitlab)
        assert not handler.handle("Issue Hook", payload)

        assert "status::outdated" not in self.issue.labels
        assert "status::new" in self.issue.labels
        assert "priority::undecided" in self.issue.labels
        assert not hasattr(self.issue, "state_event")
        self.issue.save.assert_called_once_with()
        self.issue.notes.create.assert_not_called()

    @patch("app.handlers.code_issue.get_version")
    @patch("app.handlers.code_issue.has_build_info")
    @patch("app.handlers.code_issue.fix_build_info")
    def testHandle_updateIssue_correctVersion(self, fix_build_info, has_build_info, get_version):
        self.issue.labels = ["status::incomplete-report"]
        fix_build_info.return_value = "fixed description"
        has_build_info.return_value = True
        get_version.return_value = "6.0.10"
        payload = {
            "project": {
                "id": 123,
            },
            "object_attributes": {
                "iid": 12345,
                "action": "updated",
                "assignee_id": None,
            },
        }

        handler = CodeIssueHandler(self.gitlab)
        assert not handler.handle("Issue Hook", payload)

        assert "fixed description" == self.issue.description
        assert "status::incomplete-report" not in self.issue.labels
        assert "status::new" in self.issue.labels
        assert "priority::undecided" in self.issue.labels
        assert self.issue.state_event == "reopen"
        self.issue.save.assert_called_once_with()
        self.issue.notes.create.assert_not_called()

    def testHandle_closeIssue_noMilestone(self):
        self.issue.labels = ["status::fix-committed"]
        payload = {
            "project": {
                "id": 123,
            },
            "object_attributes": {
                "iid": 12345,
                "action": "close",
                "assignee_id": None,
                "duplicated_to_id": None,
            },
        }

        handler = CodeIssueHandler(self.gitlab)
        assert not handler.handle("Issue Hook", payload)

        self.issue.save.assert_called_once_with()
        self.issue.notes.create.assert_called_once_with(
            {"body": strings.ISSUE_CLOSED_WITHOUT_MILESTONE})

    def testHandle_closeIssue_duplicate(self):
        payload = {
            "project": {
                "id": 123,
            },
            "object_attributes": {
                "iid": 12345,
                "action": "close",
                "assignee_id": None,
                "duplicated_to_id": 54321,
            },
        }

        handler = CodeIssueHandler(self.gitlab)
        assert not handler.handle("Issue Hook", payload)

        assert "status::duplicate" in self.issue.labels
        self.issue.save.assert_called_once_with()
        self.issue.notes.create.assert_called_once_with(
            {"body": strings.ISSUE_CLOSED_AS_DUPLICATE})
